/*****************************************************************************\
 * Tseng Labs ET6000, ET6100 and ET6300 graphics driver for BeOS 5.
 * Copyright (c) 2003-2004, Evgeniy Vladimirovich Bobkov.
\*****************************************************************************/

#include "setmode.h"


/*
 * ATTENTION: Currently we set the graphics modes by setting the registers
 * with the beforehand dumped values of the corresponding registers. So not
 * all graphics modes ET6x00 chips are capable of are accessible. So it would
 * be great to implement the normal algorithm of run-time computing of the
 * values to set the register.
 */


/*****************************************************************************/
typedef struct {
    uint32 VisScreenWidth;
    uint32 VisScreenHeight;
    uint8 BitsPerPlane;
    uint8 NumberGreenBits;
    uint16 Frequency;
} VIDEO_MODE_INFORMATION;
/*****************************************************************************/
/*
 * ATTENTION: Don't forget that CRTC indexed register 0x11
 * bit[7] write-protects some registers.
 */
struct {
    uint16 width, height, bpp, refreshRate;
    uint8 clock0M, clock0N;
    uint8 pci42; /* contains MCLK divider (MDIV) */
    uint8 crtc[64];
} clock0MN[] = {
    {640, 480, 24, 75, 0x28, 0x22, 0x02,
       {0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e,
        0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xea, 0x0c, 0xdf, 0xf0, 0x60, 0xe7, 0x04, 0xab,
        0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00}},
    {640, 480, 24, 72, 0x56, 0x63, 0x00,
       {0x63, 0x4f, 0x50, 0x86, 0x55, 0x9a, 0x06, 0x3e,
        0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xe8, 0x0b, 0xdf, 0xf0, 0x60, 0xe7, 0xff, 0xab,
        0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00}},
    {640, 480, 24, 60, 0x28, 0x22, 0x02,
       {0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e,
        0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xea, 0x0c, 0xdf, 0xf0, 0x60, 0xe7, 0x04, 0xab,
        0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00}},

    {640, 480, 16, 75, 0x56, 0x43, 0x01,
       {0x64, 0x4f, 0x4f, 0x88, 0x54, 0x9c, 0xf2, 0x1f,
        0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xe0, 0x03, 0xdf, 0xa0, 0x60, 0xdf, 0xf3, 0xab,
        0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00}},
    {640, 480, 16, 72, 0x56, 0x43, 0x01,
       {0x63, 0x4f, 0x50, 0x86, 0x55, 0x9a, 0x06, 0x3e,
        0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xe8, 0x0b, 0xdf, 0xa0, 0x60, 0xe7, 0xff, 0xab,
        0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00}},
    {640, 480, 16, 60, 0x28, 0x41, 0x01,
       {0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e,
        0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xea, 0x0c, 0xdf, 0xa0, 0x60, 0xe7, 0x04, 0xab,
        0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00}},

    {800, 600, 24, 75, 0x79, 0x49, 0x00,
       {0x7f, 0x63, 0x63, 0x83, 0x6b, 0x1b, 0x72, 0xf0,
        0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x58, 0x0c, 0x57, 0x2c, 0x60, 0x57, 0x73, 0xab,
        0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x80}},
    {800, 600, 24, 72, 0x28, 0x41, 0x00,
       {0x7d, 0x63, 0x63, 0x81, 0x6d, 0x1c, 0x98, 0xf0,
        0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x7c, 0x02, 0x57, 0x2c, 0x60, 0x57, 0x99, 0xab,
        0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x80}},
    {800, 600, 24, 60, 0x79, 0x49, 0x00,
       {0x7f, 0x63, 0x63, 0x83, 0x6b, 0x1b, 0x72, 0xf0,
        0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x58, 0x0c, 0x57, 0x2c, 0x60, 0x57, 0x73, 0xab,
        0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x80}},

    {800, 600, 16, 75, 0x51, 0x44, 0x00,
       {0x7f, 0x63, 0x63, 0x83, 0x68, 0x12, 0x6f, 0xf0,
        0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x58, 0x0b, 0x57, 0xc8, 0x60, 0x57, 0x70, 0xab,
        0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00}},
    {800, 600, 16, 72, 0x28, 0x41, 0x00,
       {0x7d, 0x63, 0x63, 0x81, 0x6d, 0x1c, 0x98, 0xf0,
        0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x7c, 0x02, 0x57, 0xc8, 0x60, 0x57, 0x99, 0xab,
        0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00}},
    {800, 600, 16, 60, 0x79, 0x49, 0x00,
       {0x7f, 0x63, 0x63, 0x83, 0x6b, 0x1b, 0x72, 0xf0,
        0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x58, 0x0c, 0x57, 0xc8, 0x60, 0x57, 0x73, 0xab,
        0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x00}},

    {1024, 768, 16, 75, 0x1f, 0x21, 0x00,
       {0x9f, 0x7f, 0x7f, 0x83, 0x84, 0x90, 0x1e, 0xf5,
        0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x83, 0xff, 0x00, 0x60, 0xff, 0x1f, 0xab,
        0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x80}},
    {1024, 768, 16, 70, 0x28, 0x22, 0x00,
       {0x9f, 0x7f, 0x7f, 0x83, 0x84, 0x90, 0x1e, 0xf5,
        0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x83, 0xff, 0x00, 0x60, 0xff, 0x1f, 0xab,
        0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x80}},
    {1024, 768, 16, 60, 0x6b, 0x44, 0x00,
       {0xa1, 0x7f, 0x80, 0x84, 0x88, 0x99, 0x26, 0xfd,
        0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x08, 0x0a, 0xff, 0x00, 0x60, 0x04, 0x22, 0xab,
        0xff, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x11, 0x11,
        0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x80}},
};

#define CLOCK0MN (sizeof(clock0MN) / sizeof(clock0MN[0]))
/*****************************************************************************/
__inline void et6000EnableLinearMemoryMapping(uint16 pciConfigSpace)
{
    /*
     * Relocate memory via PCI Base Address 0; don't enable MMU;
     * enable memory mapped registers; enable system linear memory mapping.
     */
    ioSet8(pciConfigSpace+0x40, 0xf0, 0x0b);
}
/*****************************************************************************/
static void setPCIConfigSpaceRegisters41to5E(uint16 pciConfigSpace,
                                             VIDEO_MODE_INFORMATION *mi,
                                             uint32 m)
{
uint8 pci415e[30] = {
    0x3a, 0x00, 0x02, 0x15, 0x04, 0x40, 0x13, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x04, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8 i;

    pci415e[1] = clock0MN[m].pci42;

    for (i=0x41; i<0x5f; i++) {
        if ((i==0x45) || ((i>0x47)&&(i<0x4e)) ||
            (i==0x4e) || ((i>0x59)&&(i<0x5c)))
            continue; /* Skip absent or read-only registers */
        ioSet8(pciConfigSpace+i, 0x00, pci415e[i-0x41]);
    }

    if (mi->BitsPerPlane == 16) {
        if (mi->NumberGreenBits == 5)
            ioSet8(pciConfigSpace+0x58, 0xfd, 0x00); /* 16bpp is 5:5:5 */
        else
            ioSet8(pciConfigSpace+0x58, 0xfd, 0x02); /* 16bpp is 5:6:5 */
    }
}
/*****************************************************************************/
static void setMiscOutputRegister(VIDEO_MODE_INFORMATION *mi) {
uint8 MiscOutputReg;
    if (mi->VisScreenHeight  < 400)
        MiscOutputReg = 0x80;           /* -vsync, +hsync */
    else if (mi->VisScreenHeight < 480)
        MiscOutputReg = 0x40;           /* +vsync, -hsync */
    else if (mi->VisScreenHeight < 768)
        MiscOutputReg = 0xc0;           /* -vsync, -hsync */
    else
        MiscOutputReg = 0x00;           /* +vsync, +hsync */
    ioSet8(0x3c2, 0x00, (ioGet8(0x3cc) & 0x3f) | MiscOutputReg);

    /* Enable host access to display memory, color mode */
    ioSet8(0x3c2, 0x00, (ioGet8(0x3cc) & 0xfc) | 0x03);
}
/*****************************************************************************/
static void setATC(uint8 bpp) {
uint8 atc[7] = {0x21, 0x00, 0x30, 0x00, 0x00}, atc16 = 0x80;///
///uint8 atc[7] = {0x01, 0x00, 0x0f, 0x00, 0x00}, atc16 = 0x80;///zzz
uint8 i, atcIndexReg;
volatile uint8 f;

    f = ioGet8(0x3da); /* Set index/data flip-flop to index mode */
    atcIndexReg = ioGet8(0x3c0) & 0xe0; /* Save bits[7:5] */

    for (i = 0x10; i < 0x15; i++) {
        f = ioGet8(0x3da); /* Set index/data flip-flop to index mode */
        ioSet8(0x3c0, 0x00, i | atcIndexReg);
        ioSet8(0x3c0, 0x00, atc[i-0x10]);
    }

    switch (bpp) {
    case 24:
        atc16 |= 0x20;
        break;
    case 16:
        atc16 |= 0x10;
        break;
    }
    f = ioGet8(0x3da); /* Set index/data flip-flop to index mode */
    ioSet8(0x3c0, 0x00, 0x16 | atcIndexReg);
    ioSet8(0x3c0, 0x00, atc16);
}
/*****************************************************************************/
static void setTS(void) {
uint8 ts[7] = {0x02, 0x01, 0x0f, 0x00, 0x0e, 0x00, 0x00};
uint8 i;

    for (i = 0; i < 7; i++) {
        if (i == 5) continue; /* Skip absent register */
        ioSet8(0x3c4, 0xf8, i);
        ioSet8(0x3c5, 0x00, ts[i]);
    }
}
/*****************************************************************************/
static void setGDC(void) {
uint8 gdc[9] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0f, 0xff};
uint8 i;

     for (i = 0; i < 9; i++) {
        ioSet8(0x3ce, 0xf0, i);
        ioSet8(0x3cf, 0x00, gdc[i]);
    }
}
/*****************************************************************************/
static void setClock0RegNum(uint8 regNum) {
    /* Set bits[1:0] of the selected CLOCK0 PLL parameters register number */
    ioSet8(0x3c2, 0x00, (ioGet8(0x3cc) & 0xf3) | ((regNum & 0x03) << 2));

    /* Set bit[2] of the selected CLOCK0 PLL parameters register number */
    ioSet8(0x3d4, 0xc0, 0x34);
    ioSet8(0x3d5, 0xfd, (regNum & 0x04) << 1);
}
/*****************************************************************************/
static void setPLL(uint16 pciConfigSpace,
                   uint32 m) /* mode index */
{
uint8 regNum = 3;
uint8 clock0M = 0, clock0N = 0;

    clock0M = clock0MN[m].clock0M;
    clock0N = clock0MN[m].clock0N;

    setClock0RegNum(regNum);
    ioSet8(pciConfigSpace+0x67, 0x00, regNum);
    ioSet8(pciConfigSpace+0x68, 0x00, regNum);
    ioSet8(pciConfigSpace+0x69, 0x00, clock0M);
    ioSet8(pciConfigSpace+0x69, 0x00, clock0N);
}
/*****************************************************************************/
static void setCRTC(uint32 m) /* mode index */
{
uint8 i;

    /* Unlock the write protection of several registers */
    ioSet8(0x3d4, 0xc0, 0x11);
    ioSet8(0x3d5, 0x7f, 0x00);

    for (i = 0; i < 64; i++) {
        if (((i > 0x18) && (i < 0x33)) ||
            ((i > 0x35) && (i < 0x3f)))
            continue; /* Skip absent or read-only registers */
        ioSet8(0x3d4, 0xc0, i);
        ioSet8(0x3d5, 0x00, clock0MN[m].crtc[i]);
    }
}
/*****************************************************************************/
uint32 et6000SetGraphicsMode(VIDEO_MODE_INFORMATION *mi,
                            uint16 pciConfigSpace)
{
uint8 m;

    for(m = 0; m < CLOCK0MN; m++) {
        if ((clock0MN[m].width == mi->VisScreenWidth) &&
            (clock0MN[m].height == mi->VisScreenHeight) &&
            (clock0MN[m].bpp == mi->BitsPerPlane) &&
            ((clock0MN[m].refreshRate-1 <= mi->Frequency) &&
            (clock0MN[m].refreshRate+1 >= mi->Frequency)))
        {
            break;
        }
    }
    if (m == CLOCK0MN)
        return B_BAD_VALUE; /* Found no entry for requested mode */

    et6000EnableLinearMemoryMapping(pciConfigSpace);
    setMiscOutputRegister(mi);
    ioSet8(0x3d8, 0x00, 0xa0); /* Set the KEY for color modes */
    setPCIConfigSpaceRegisters41to5E(pciConfigSpace, mi, m);
    ioSet8(0x3c6, 0x00, 0xff); /* Set pixel mask */
    setATC(mi->BitsPerPlane);
    setTS();
    setGDC();
    setCRTC(m);
    setPLL(pciConfigSpace, m);

    return B_OK;
}
/*****************************************************************************/
status_t et6000SetMode(display_mode *mode, uint16 pciConfigSpace) {
VIDEO_MODE_INFORMATION mi;

    mi.VisScreenWidth = mode->virtual_width;
    mi.VisScreenHeight = mode->virtual_height;

    switch (mode->space) {
        case B_RGB24_LITTLE:
        case B_RGB24_BIG:
            mi.BitsPerPlane = 24;
            mi.NumberGreenBits = 8;
            break;
        case B_RGB16_LITTLE:
        case B_RGB16_BIG:
            mi.BitsPerPlane = 16;
            mi.NumberGreenBits = 6;
            break;
        case B_RGB15_LITTLE:
        case B_RGB15_BIG:
            mi.BitsPerPlane = 16;
            mi.NumberGreenBits = 5;
            break;
    }

    mi.Frequency = (uint16) (mode->timing.pixel_clock * 1000
        / (mode->timing.h_total * mode->timing.v_total));

    return et6000SetGraphicsMode(&mi, pciConfigSpace);
}
/*****************************************************************************/
status_t et6000ProposeMode(display_mode *mode, uint32 memSize) {
uint8 m, bpp;
uint16 refreshRate;

    /* Framebuffer must not overlap with the memory mapped registers */
    if (memSize > 0x3fe000)
        memSize = 0x3fe000;

    memSize -= ET6000_ACL_NEEDS_MEMORY;

    switch (mode->space) {
        case B_RGB24_LITTLE:
        case B_RGB24_BIG:
            bpp = 24;
            break;
        case B_RGB16_LITTLE:
        case B_RGB16_BIG:
        case B_RGB15_LITTLE:
        case B_RGB15_BIG:
            bpp = 16;
            break;
    }

    refreshRate = (uint16) (mode->timing.pixel_clock * 1000
                  / (mode->timing.h_total * mode->timing.v_total));

    for(m = 0; m < CLOCK0MN; m++) {
        if ((clock0MN[m].width == mode->virtual_width) &&
            (clock0MN[m].height == mode->virtual_height) &&
            (clock0MN[m].bpp == bpp) &&
            ((clock0MN[m].refreshRate-1 <= refreshRate) &&
            (clock0MN[m].refreshRate+1 >= refreshRate)))
        {
            break;
        }
    }
    if (m == CLOCK0MN)
        return B_BAD_VALUE; /* Found no entry for requested mode */

    if (mode->virtual_width * mode->virtual_height * bpp / 8 > memSize)
        return B_BAD_VALUE; /* Not enough adapter onboard memory */

    return B_OK;
}
/*****************************************************************************/
